#pragma once
#include <iostream>
#include "Common.hpp"
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <cstdlib>
#include <functional>
#include "Log.hpp"
#include "Internet.hpp"
#include "ThreadPool.hpp"


#define BackWait 8

using namespace LogModule;
using namespace ThreadPoolModule;
using task_t = std::function<void()>;
using handler_t = std::function<std::string(std::string& tmp)>;

uint16_t defaultport = 8080;
std::string defaultip = "127.0.0.1";

class TcpServer
{
    struct Thread 
    {
        int sockfd;
        TcpServer* self;
    };
public:
    TcpServer(handler_t hander, uint16_t port = defaultport, std::string ip = defaultip)
        : _port(port), _ip(ip), _isrunning(false), _listensockfd(-1)
        ,_hander(hander)
    {
    }
    ~TcpServer()
    {
    }
    void InitServer()
    {
        _listensockfd = ::socket(AF_INET,SOCK_STREAM,0);
        if(_listensockfd < 0)
        {
            LOG(LogLevel::FATAL) << "create sockfd false";
            Die(1);
        }
        LOG(LogLevel::INFO) << "socket create success, sockfd is : " << _listensockfd;

        struct sockaddr_in local;
        bzero(&local, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = ::htons(_port);
        local.sin_addr.s_addr = INADDR_ANY;

        

        // //面向连接的，还要等待随时被连接
        // //所以要设置为监听模式
        
       

        // 2. bind
        int n = ::bind(_listensockfd,CONV(&local),sizeof(local));

        if (n < 0)
        {
            LOG(LogLevel::FATAL) << "bind error";
            Die(BIND_ERR);
        }
        LOG(LogLevel::INFO) << "bind success, sockfd is : " << _listensockfd;
        // 3. cs,tcp是面向连接的，就要求tcp随时随地等待被连接
        // tcp 需要将socket设置成为监听状态

        int m = ::listen(_listensockfd,8); 
        if(m < 0 )
        {
            LOG(LogLevel::FATAL) <<"监听失败";
            Die(LISTEN_ERR);
        }
        LOG(LogLevel::FATAL) <<"监听成功";
        
    }
    void HandlerRequest(int sockfd)
    {
        char buffer[1024];
        std::string package;
        while(true)
        {
            //接受消息
            ssize_t n = ::read(sockfd,buffer,sizeof(buffer)-1);
            if(n>0)
            {
                buffer[n] = 0;
                LOG(LogLevel::DEBUG)<<"接受到消息了#:"<<buffer;
                package += buffer;

                std::string cmd_result = _hander(package);
               
                ::send(sockfd, cmd_result.c_str(), cmd_result.size(), 0); // 写入也是不完善
            }
            else if(n == 0)
            {
                LOG(LogLevel::INFO) << "client quit: " << sockfd;
                break;
            }
            else
                break;

        }
    }
    static void* ThreadHandler(void* args)
    {
        //用线程也要等待回收（join） 必须等待回收的话就会阻塞，所以让线程自己结束释放资源
        pthread_detach(pthread_self());
        Thread* tmp = (Thread*)args;
        tmp->self->HandlerRequest(tmp->sockfd);
        return nullptr;

    }
    void Start()
    {
        _isrunning = true;
        
        while(true)
        {
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sockfd = ::accept(_listensockfd,CONV(&peer),&len);
            //建立连接之后，这个对应的文件描述符才负责传信（接待）
            if (sockfd < 0)
            {
                LOG(LogLevel::WARNING) << "accept error: " << strerror(errno);
                continue;
            }
            LOG(LogLevel::INFO) << "accept success, sockfd is : " << sockfd;
            InetAddr inetaddr(peer);
            LOG(LogLevel::INFO) << "client info: " << inetaddr.Addr();


            //version 0
            //HandlerRequest(sockfd);

            // version -1 多线程版本
            // pid_t pid = fork();
            //::signal(SIGCHLD,SIG_IGN);
            // if(pid == 0)
            // {
            //     //子进程再创建孙子进程，子进程直接退掉，由系统进程1 来回收管理孙子进程
            //     //子进程和父进程 各有一张文件描述符表 文件都是通过引用计数进行管理的
            //     //就像管道一样
            //     ::close(_listensockfd);
            //     if(fork() > 0)
            //     {
            //         exit(0);
            //     }
            //     HandlerRequest(sockfd);
            //     exit(0);
            // }
            // 给出建议父进程不要关闭文件描述符，这个设计叫权责分明
            // 现在语法执行没错，如果修改内容容易有错
            // ::close(sockfd);
            
            // pid_t waitid = ::waitpid(pid,nullptr,0);
            // if(waitid<0)
            // {
            //     LOG(LogLevel::ERROR)<<"回收父进程失败";
            // }
        //version 2 用多线程   
        // pthread_t pid;
        // Thread* data = new Thread;
        // data->self = this;
        // data->sockfd = sockfd;
        // pthread_create(&pid,nullptr,ThreadHandler,data);
        
        //version 3 线程池
           task_t f = std::bind(&TcpServer::HandlerRequest,this,sockfd);
            ThreadPool<task_t>::getInstance()->Equeue([&sockfd,this](){
                this->HandlerRequest(sockfd);
           });
       }

    }

private:
    int _listensockfd; //这个文件描述符 只负责监听（也就是送客人，不负责招待）
    uint16_t _port;
    std::string _ip;
    bool _isrunning;
    handler_t _hander;
};

